CO emissions (rule discovery)

Load the packages

library("gramEvol")
library("tidyverse")
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.5     ✓ purrr   0.3.4
## ✓ tibble  3.1.5     ✓ dplyr   1.0.7
## ✓ tidyr   1.1.3     ✓ stringr 1.4.0
## ✓ readr   2.0.0     ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library("patchwork")
library("plotly")
## 
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout

Load and scale the data

data <- read_csv("/Users/tbotsas/Documents/Research/datasets_rml/pp_gas_emission/pp_gas_emission/gt_2013.csv", col_names = TRUE)
## Rows: 7152 Columns: 11
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (11): AT, AP, AH, AFDP, GTEP, TIT, TAT, TEY, CDP, CO, NOX
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
data %>% glimpse()
## Rows: 7,152
## Columns: 11
## $ AT   <dbl> 9.3779, 9.2985, 9.1337, 8.9715, 9.0157, 9.0465, 8.8649, 8.9862, 8…
## $ AP   <dbl> 1020.1, 1019.9, 1019.8, 1019.3, 1019.1, 1019.0, 1019.0, 1019.2, 1…
## $ AH   <dbl> 90.262, 89.934, 89.868, 89.490, 89.099, 88.783, 88.853, 88.760, 8…
## $ AFDP <dbl> 2.3927, 2.3732, 2.3854, 2.3825, 2.4044, 2.3826, 2.4237, 2.4409, 2…
## $ GTEP <dbl> 19.166, 19.119, 19.178, 19.180, 19.206, 19.304, 19.269, 19.446, 1…
## $ TIT  <dbl> 1043.6, 1039.9, 1041.0, 1037.1, 1043.5, 1042.5, 1043.5, 1043.7, 1…
## $ TAT  <dbl> 541.16, 538.94, 539.47, 536.89, 541.25, 540.14, 540.87, 540.65, 5…
## $ TEY  <dbl> 110.16, 109.23, 109.62, 108.88, 110.09, 110.23, 110.53, 110.61, 1…
## $ CDP  <dbl> 10.564, 10.572, 10.543, 10.458, 10.464, 10.461, 10.480, 10.475, 1…
## $ CO   <dbl> 9.3472, 11.0160, 10.7500, 12.2870, 9.8229, 10.3360, 9.8101, 9.464…
## $ NOX  <dbl> 98.741, 104.290, 103.470, 108.810, 100.020, 96.442, 97.125, 93.05…
cut_data <- data %>% filter(AH >= 97)
data_scale <- scale(cut_data)
(features <- attributes(data_scale))
## $dim
## [1] 547  11
## 
## $dimnames
## $dimnames[[1]]
## NULL
## 
## $dimnames[[2]]
##  [1] "AT"   "AP"   "AH"   "AFDP" "GTEP" "TIT"  "TAT"  "TEY"  "CDP"  "CO"  
## [11] "NOX" 
## 
## 
## $`scaled:center`
##          AT          AP          AH        AFDP        GTEP         TIT 
##   12.645636 1008.919049   98.866040    3.247599   24.074249 1074.692870 
##         TAT         TEY         CDP          CO         NOX 
##  545.311316  129.149415   11.676402    3.692881   74.370018 
## 
## $`scaled:scale`
##         AT         AP         AH       AFDP       GTEP        TIT        TAT 
##  3.6305288  6.4425392  1.1004917  0.6059533  4.8234037 19.7331791  7.2961815 
##        TEY        CDP         CO        NOX 
## 18.3365764  1.2582708  2.9473063 13.0408933
scaled_data <- as.data.frame(data_scale)
scaled_data %>% glimpse()
## Rows: 547
## Columns: 11
## $ AT   <dbl> -2.01153510, -1.84062336, -1.23487139, -0.21694806, -0.23182193, …
## $ AP   <dbl> 2.12353393, 1.71996635, -0.32891524, -0.11160962, -0.15817511, -0…
## $ AH   <dbl> -0.76514911, -1.44030188, -0.96505976, -0.42257496, -0.13724794, …
## $ AFDP <dbl> -0.64773843, -0.51488992, -1.38904963, -0.19770378, -0.73487385, …
## $ GTEP <dbl> -0.325962478, -0.060797032, -1.024017252, 0.030424858, -0.5208456…
## $ TIT  <dbl> -0.2631543, 0.4817840, -1.3932307, 0.5273925, -0.4354529, -0.9422…
## $ TAT  <dbl> -0.01799794, 0.63713927, -0.08104462, 0.62754521, 0.39865836, 0.4…
## $ TEY  <dbl> -0.1957516, 0.2852542, -1.0050630, 0.2596223, -0.4504339, -0.8501…
## $ CDP  <dbl> -0.24033143, 0.05531241, -1.00725699, 0.17849735, -0.46047483, -0…
## $ CO   <dbl> -0.08464728, -0.64220724, 1.32721818, -0.38383572, 0.29573395, 0.…
## $ NOX  <dbl> 2.0558393628, 0.2541989762, 0.9875843202, 0.2179284546, 0.4430664…
rescale <- function(input, var){
  res = input*features$`scaled:scale`[match(var,features$dimnames[[2]])]+features$`scaled:center`[match(var,features$dimnames[[2]])]
  return (res %>% as.numeric())
}

Plot the data

ggplot(data) +
  geom_point(aes(x=AH, y=CO)) +
  geom_point(data = scaled_data, aes(rescale(AH,"AH"), rescale(CO,"CO")), col = "green")

Piece-wise regression rules

  • Define the grammar
ruleDef <- list(
  res = grule( ifelse(expr, expr_y, expr_y) ),
  expr = grule(comparison(var, num)),
  expr_y = grule(op(var, var), op(var, num_regr), num_regr),
  comparison = grule(`>`, `<`, `>=`, `<=`),
  op = grule(`+`,`-`,`*`),
  var = grule(scaled_data$TEY),
  #var = grule(scaled_data$TIT, scaled_data$CDP, scaled_data$GTEP, scaled_data$AT, scaled_data$AP, scaled_data$AH, scaled_data$AFDP, scaled_data$TAT, scaled_data$TEY),
  num_regr = gvrule(seq(-5.,5., by = .05)),
  num = gvrule(seq(-2.,2., by = .05)))

grammarDef <- CreateGrammar(ruleDef)

set.seed(123)
GrammarRandomExpression(grammarDef, 6)
## [[1]]
## expression(ifelse(scaled_data$TEY >= 1.8, scaled_data$TEY - scaled_data$TEY, 
##     scaled_data$TEY + 1.8))
## 
## [[2]]
## expression(ifelse(scaled_data$TEY > -1.85, scaled_data$TEY + 
##     1.95, 5))
## 
## [[3]]
## expression(ifelse(scaled_data$TEY < 1.9, scaled_data$TEY - scaled_data$TEY, 
##     scaled_data$TEY * -1.8))
## 
## [[4]]
## expression(ifelse(scaled_data$TEY >= -0.5, scaled_data$TEY - 
##     scaled_data$TEY, scaled_data$TEY - -0.55))
## 
## [[5]]
## expression(ifelse(scaled_data$TEY >= -1.5, 4, scaled_data$TEY * 
##     -1.15))
## 
## [[6]]
## expression(ifelse(scaled_data$TEY <= -0.2, 1.3, -5))
  • Define the cost function
symRegCostFunc <- function(res) {
  result <- suppressWarnings(eval(res))
  if (any(is.nan(result)))
    return (Inf)
  return (sum((scaled_data$CO - result)^2))}
  • Run the Grammatical evolution algorithm
set.seed(123)
ge <- GrammaticalEvolution(grammarDef, symRegCostFunc, iterations = 10000)
ge
## Grammatical Evolution Search Results:
##   No. Generations:  10000 
##   Best Expression:  ifelse(scaled_data$TEY < 0.15, scaled_data$TEY * -0.899999999999999, -0.7) 
##   Best Cost:        210.193695408668

Plot the rules

#  Best Expression:  ifelse(scaled_data$TIT < -1.2, scaled_data$TIT * -1.2, scaled_data$TIT * -0.7) 
#Best Cost:        0.250503405639915 137.025362885033 


xv2 <- c(min(scaled_data$TIT), -1.2, max(scaled_data$TIT))
yv2 <- c(-1.2*xv2[1:2], -0.7*xv2[2:3])

pl1 <- ggplot(data) +
  geom_point(aes(TIT, CO)) +
  geom_vline(xintercept = rescale(xv2[2],"TIT"), color = "red", lty = 2) +
  geom_point(data = scaled_data, aes(rescale(TIT,"TIT"), rescale(CO,"CO")), col = "green") + 
  geom_segment(aes(x = rescale(xv2[1],"TIT"), xend = rescale(xv2[2],"TIT"), y = rescale(yv2[1],"CO"), yend = rescale(yv2[2],"CO")), color = "red", size = 1.5) +
  geom_segment(aes(x = rescale(xv2[2],"TIT"), xend = rescale(xv2[3],"TIT"), y = rescale(yv2[3],"CO"), yend = rescale(yv2[4],"CO")), color = "red", size = 1.5) +
  ggtitle(paste("Cost:", 137.026 )) +
  theme(plot.title = element_text(hjust = 0.5))

#Best Expression:  ifelse(scaled_data$TEY < 0.15, scaled_data$TEY * -0.899999999999999, -0.7) 
#Best Cost:        0.384266353580746 210.193695408668

xv1 <- c(min(scaled_data$TEY), 0.15, max(scaled_data$TEY))
yv1 <- c(-0.9*xv1[1:2], rep(-0.7,2))

pl3 <- ggplot(data) +
  geom_point(aes(TEY, CO)) +
  geom_vline(xintercept = rescale(xv1[2],"TEY"), color = "red", lty = 2) +
  geom_point(data = scaled_data, aes(rescale(TEY,"TEY"), rescale(CO,"CO")), col = "green") + 
  geom_segment(aes(x = rescale(xv1[1],"TEY"), xend = rescale(xv1[2],"TEY"), y = rescale(yv1[1],"CO"), yend = rescale(yv1[2],"CO")), color = "red", size = 1.5) +
  geom_segment(aes(x = rescale(xv1[2],"TEY"), xend = rescale(xv1[3],"TEY"), y = rescale(yv1[3],"CO"), yend = rescale(yv1[4],"CO")), color = "red", size = 1.5) +
  ggtitle(paste("Cost:", 210.19)) +
  theme(plot.title = element_text(hjust = 0.5))

#  Best Expression:  ifelse(scaled_data$GTEP > -0.2, -0.7, scaled_data$GTEP * scaled_data$GTEP) 
#Best Cost:        0.379436604356225 207.551822582855

ff_GTEP <- function(x) rescale(((x-features$`scaled:center`[5])/features$`scaled:scale`[5])^2,"CO")

xv4 <- c(min(scaled_data$GTEP), -0.2, max(scaled_data$GTEP))
yv4 <- c(xv4[1:2], rep(-0.7,2))

pl2 <- ggplot(data) +
  geom_point(aes(GTEP, CO)) +
  geom_vline(xintercept = rescale(xv4[2],"GTEP"), color = "red", lty = 2) +
  geom_point(data = scaled_data, aes(rescale(GTEP,"GTEP"), rescale(CO,"CO")), col = "green") + 
  stat_function(fun=ff_GTEP, xlim = c(rescale(xv4[1],"GTEP"), rescale(xv4[2],"GTEP")), colour="red", size = 1.5) + 
  geom_segment(aes(x = rescale(xv4[2],"GTEP"), xend = rescale(xv4[3],"GTEP"), y = rescale(yv4[3],"CO"), yend = rescale(yv4[4],"CO")), color = "red", size = 1.5) +
  ggtitle(paste("Cost:", 207.55)) +
  theme(plot.title = element_text(hjust = 0.5))

#  Best Expression:  ifelse(scaled_data$CDP <= -0.2, scaled_data$CDP * -0.95, -0.7) 
#Best Cost:        0.39378936523437 215.4027827832 


xv3 <- c(min(scaled_data$CDP), -0.2, max(scaled_data$CDP))
yv3 <- c(-0.95*xv3[1:2], rep(-0.7,2))

pl4 <- ggplot(data) +
  geom_point(aes(CDP, CO)) +
  geom_vline(xintercept = rescale(xv3[2],"CDP"), color = "red", lty = 2) +
  geom_point(data = scaled_data, aes(rescale(CDP,"CDP"), rescale(CO,"CO")), col = "green") + 
  geom_segment(aes(x = rescale(xv3[1],"CDP"), xend = rescale(xv3[2],"CDP"), y = rescale(yv3[1],"CO"), yend = rescale(yv3[2],"CO")), color = "red", size = 1.5) +
  geom_segment(aes(x = rescale(xv3[2],"CDP"), xend = rescale(xv3[3],"CDP"), y = rescale(yv3[3],"CO"), yend = rescale(yv3[4],"CO")), color = "red", size = 1.5) +
  ggtitle(paste("Cost:", 215.4 )) +
  theme(plot.title = element_text(hjust = 0.5))


#Best Expression:  ifelse(scaled_data$TAT <= 0.5, scaled_data$TAT + 1.1, scaled_data$TAT * -0.199999999999999) 
#Best Cost:        0.665537386100754  364.048950197112


xv6 <- c(min(scaled_data$TAT), 0.5, max(scaled_data$TAT))
yv6 <- c(xv6[1:2]+1.1, -0.2*xv6[2:3])

pl6 <- ggplot(data) +
  geom_point(aes(TAT, CO)) +
  geom_vline(xintercept = rescale(xv6[2],"TAT"), color = "red", lty = 2) +
  geom_point(data = scaled_data, aes(rescale(TAT,"TAT"), rescale(CO,"CO")), col = "green") + 
  geom_segment(aes(x = rescale(xv6[1],"TAT"), xend = rescale(xv6[2],"TAT"), y = rescale(yv6[1],"CO"), yend = rescale(yv6[2],"CO")), color = "red", size = 1.5) +
  geom_segment(aes(x = rescale(xv6[2],"TAT"), xend = rescale(xv6[3],"TAT"), y = rescale(yv6[3],"CO"), yend = rescale(yv6[4],"CO")), color = "red", size = 1.5) +
  ggtitle(paste("Cost:", 364.05)) +
  theme(plot.title = element_text(hjust = 0.5))


#  Best Expression:  ifelse(scaled_data$AT > 1.35, 0.550000000000001, -0.0499999999999998) 
#Best Cost:        0.963166150643463 526.851884401974 

xv5 <- c(min(scaled_data$AT), 1.35, max(scaled_data$AT))
yv5 <- c(rep(0.5,2), rep(0.55,2))

pl8 <- ggplot(data) +
  geom_point(aes(AT, CO)) +
  geom_vline(xintercept = rescale(xv3[2],"AT"), color = "red", lty = 2) +
  geom_point(data = scaled_data, aes(rescale(AT,"AT"), rescale(CO,"CO")), col = "green") + 
  geom_segment(aes(x = rescale(xv5[1],"AT"), xend = rescale(xv5[2],"AT"), y = rescale(yv5[1],"CO"), yend = rescale(yv5[2],"CO")), color = "red", size = 1.5) +
  geom_segment(aes(x = rescale(xv5[2],"AT"), xend = rescale(xv5[3],"AT"), y = rescale(yv5[3],"CO"), yend = rescale(yv5[4],"CO")), color = "red", size = 1.5) +
  ggtitle(paste("Cost:", 526.85)) +
  theme(plot.title = element_text(hjust = 0.5))


# Best Expression:  ifelse(scaled_data$AP >= 0.35, scaled_data$AP * -0.25, 0.15) 
#Best Cost:        0.959066570388235 524.609414002364

xv7 <- c(min(scaled_data$AP), 0.35, max(scaled_data$AP))
yv7 <- c(rep(0.15,2), -0.25*xv7[2:3])

pl7 <- ggplot(data) +
  geom_point(aes(AP, CO)) +
  geom_vline(xintercept = rescale(xv7[2],"AP"), color = "red", lty = 2) +
  geom_point(data = scaled_data, aes(rescale(AP,"AP"), rescale(CO,"CO")), col = "green") + 
  geom_segment(aes(x = rescale(xv7[1],"AP"), xend = rescale(xv7[2],"AP"), y = rescale(yv7[1],"CO"), yend = rescale(yv7[2],"CO")), color = "red", size = 1.5) +
  geom_segment(aes(x = rescale(xv7[2],"AP"), xend = rescale(xv7[3],"AP"), y = rescale(yv7[3],"CO"), yend = rescale(yv7[4],"CO")), color = "red", size = 1.5) +
  ggtitle(paste("Cost:", 524.61)) +
  theme(plot.title = element_text(hjust = 0.5))


#  Best Expression:  ifelse(scaled_data$AH >= -1.55, scaled_data$AH * -0.0499999999999998, -0.199999999999999) 
#Best Cost:        0.993710184870688 543.559471124266 

xv8 <- c(min(scaled_data$AH), -1.55, max(scaled_data$AH))
yv8 <- c(rep(-0.2,2), -0.05*xv8[2:3])

pl9 <- ggplot(data) +
  geom_point(aes(AH, CO)) +
  geom_vline(xintercept = rescale(xv8[2],"AH"), color = "red", lty = 2) +
  geom_point(data = scaled_data, aes(rescale(AH,"AH"), rescale(CO,"CO")), col = "green") + 
  geom_segment(aes(x = rescale(xv8[1],"AH"), xend = rescale(xv8[2],"AH"), y = rescale(yv8[1],"CO"), yend = rescale(yv8[2],"CO")), color = "red", size = 1.5) +
  geom_segment(aes(x = rescale(xv8[2],"AH"), xend = rescale(xv8[3],"AH"), y = rescale(yv8[3],"CO"), yend = rescale(yv8[4],"CO")), color = "red", size = 1.5) +
  geom_segment(aes(x = min(data$AH), xend = rescale(xv8[2],"AH"), y = rescale(yv8[1],"CO"), yend = rescale(yv8[2],"CO")), color = "yellow", size = 1.5) +
  ggtitle(paste("Cost:", 543.56)) +
  theme(plot.title = element_text(hjust = 0.5))

#  Best Expression:  ifelse(scaled_data$AFDP >= -0.5, -0.55, scaled_data$AFDP * -0.95) 
#Best Cost:        0.465598872252407 254.682583122067 

xv9 <- c(min(scaled_data$AFDP), -.5, max(scaled_data$AFDP))
yv9 <- c(-0.95*xv9[1:2], rep(-0.55,2))

pl5 <- ggplot(data) +
  geom_point(aes(AFDP, CO)) +
  geom_vline(xintercept = rescale(xv9[2],"AFDP"), color = "red", lty = 2) +
  geom_point(data = scaled_data, aes(rescale(AFDP,"AFDP"), rescale(CO,"CO")), col = "green") + 
  geom_segment(aes(x = rescale(xv9[1],"AFDP"), xend = rescale(xv9[2],"AFDP"), y = rescale(yv9[1],"CO"), yend = rescale(yv9[2],"CO")), color = "red", size = 1.5) +
  geom_segment(aes(x = rescale(xv9[2],"AFDP"), xend = rescale(xv9[3],"AFDP"), y = rescale(yv9[3],"CO"), yend = rescale(yv9[4],"CO")), color = "red", size = 1.5) +
  ggtitle(paste("Cost:", 254.68)) +
  theme(plot.title = element_text(hjust = 0.5))


p_final <- (pl1+ pl2 + pl3)/(pl4 + pl5 + pl6)/(pl7 + pl8 + pl9)
p_final

#ggsave("CO_rulesRegr.png", p_final, width = 16, height = 16, dpi = 300)

Intersection rules

  • Define the grammar
ruleDef <- list(
  res = grule( ifelse((expr) != (comparison(scaled_data$CO, (d=num))), d, scaled_data$CO) ),
  expr = grule(comparison(var, num), union(expr,expr)),
  union = grule(`&`),
  comparison = grule(`>`, `<`, `>=`, `<=`),
  var = grule(scaled_data$AT, scaled_data$AP, scaled_data$AH, scaled_data$AFDP, scaled_data$CDP, scaled_data$TAT),
  num = gvrule(seq(-.75,.75, by = .05)))

grammarDef <- CreateGrammar(ruleDef)
grammarDef
## <res>        ::= ifelse((<expr>) != (<comparison>(scaled_data$CO, (d = <num>))), d, scaled_data$CO)
## <expr>       ::= <comparison>(<var>, <num>) | <union>(<expr>, <expr>)
## <union>      ::= `&`
## <comparison> ::= `>` | `<` | `>=` | `<=`
## <var>        ::= scaled_data$AT | scaled_data$AP | scaled_data$AH | scaled_data$AFDP | scaled_data$CDP | scaled_data$TAT
## <num>        ::= -0.75 | -0.7 | -0.65 | -0.6 | -0.55 | -0.5 | -0.45 | -0.4 | -0.35 | -0.3 | -0.25 | -0.2 | -0.15 | -0.1 | -0.0499999999999999 | 0 | 0.05 | 0.1 | 0.15 | 0.2 | 0.25 | 0.3 | 0.35 | 0.4 | 0.45 | 0.5 | 0.55 | 0.6 | 0.65 | 0.7 | 0.75
set.seed(123)
GrammarRandomExpression(grammarDef, 6)
## [[1]]
## expression(ifelse((scaled_data$TAT >= 0.7) != (scaled_data$CO > 
##     (d = 0.05)), d, scaled_data$CO))
## 
## [[2]]
## expression(ifelse((scaled_data$TAT >= -0.55) != (scaled_data$CO <= 
##     (d = -0.45)), d, scaled_data$CO))
## 
## [[3]]
## expression(ifelse((scaled_data$AT <= 0 & (scaled_data$AP < 0.7 & 
##     scaled_data$AT >= 0.35)) != (scaled_data$CO < (d = 0.1)), 
##     d, scaled_data$CO))
## 
## [[4]]
## expression(ifelse((scaled_data$TAT <= 0) != (scaled_data$CO >= 
##     (d = -0.35)), d, scaled_data$CO))
## 
## [[5]]
## expression(ifelse((scaled_data$AFDP <= -0.15 & (scaled_data$AP < 
##     0 & scaled_data$CDP <= -0.7) & scaled_data$AH <= 0.5) != 
##     (scaled_data$CO >= (d = -0.3)), d, scaled_data$CO))
## 
## [[6]]
## expression(ifelse((scaled_data$AH <= -0.4 & scaled_data$AFDP > 
##     0.45) != (scaled_data$CO < (d = -0.1)), d, scaled_data$CO))
  • Define the cost function
symRegCostFunc <- function(res) {
  result <- suppressWarnings(eval(res))
  if (any(is.nan(result)))
    return (Inf)
  return (sum((scaled_data$CO - result)^2))}
  • Run the Grammatical evolution algorithm
set.seed(123)
ge <- GrammaticalEvolution(grammarDef, symRegCostFunc)
ge
## Grammatical Evolution Search Results:
##   No. Generations:  1000 
##   Best Expression:  ifelse((scaled_data$CDP >= -0.15 & scaled_data$TAT < -0.55) != (scaled_data$CO <= (d = -0.75)), d, scaled_data$CO) 
##   Best Cost:        2.99232005220697

Plot the rule

plt <- plot_ly(x=data$CDP, y=data$TAT, z=data$CO, type="scatter3d", mode="markers", color = data$CO)
plt <- add_trace(p = plt,
                 z = rep(seq(min(data$CO),max(data$CO), length.out = 100),100),
                 x = rep(rescale(-0.15,"CDP"), 10000),
                 y = rep(seq(min(data$TAT),max(data$TAT), length.out = 100),each = 100),
                 type="scatter3d", mode="markers", color = "green")

plt <- add_trace(p = plt,
                 z = rep(seq(min(data$CO),max(data$CO), length.out = 100),100),
                 x = rep(seq(min(data$CDP),max(data$CDP), length.out = 100),each = 100),
                 y = rep(rescale(-0.55,"TAT"), 10000),
                 type="scatter3d", mode="markers", color = "green")

plt
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels